2 Part II Advanced displays

2.1 Preparation

You will find the material for this lecture at https://github.com/cbhurley/CRT2021vis

First install these packages

install.packages(c("GGally", "ISLR2", "naniar"))

and load with

library(GGally)
library(ISLR2)
library(timetk) # for the data
library(palmerpenguins)
library(tidyverse)
library(naniar)

bike <- bike_sharing_daily
bike$season <- c("Winter","Spring", "Summer","Fall")[bike$season]
bike$season <- factor(bike$season, levels = c("Winter","Spring", "Summer","Fall"))  

bike$mnth <- factor(bike$mnth)

bike$holiday <- factor(c("No","Yes")[bike$holiday+1])
bike$workingday <- factor(c("No","Yes")[bike$workingday+1])

bike$yr <- factor(c("2011","2012")[bike$yr+1])

bike$weathersit <- c("clear", "cloudy", "lightP", "heavyP")[bike$weathersit]
bike$weathersit <- factor(bike$weathersit, levels= c("clear", "cloudy", "lightP", "heavyP"))
bike <- droplevels(bike)

2.2 Big data

ISLR2 has an hourly version of the bike data with 8645 observations.

The plot of registered versuscasual users is

ggplot(data=Bikeshare, aes(x=casual, y=registered)) + geom_point()

There is likely lot of overplotting.

Jittering will randomly undo rounding, and using alpha will help too

ggplot(data=Bikeshare, aes(x=casual, y=registered)) + 
  geom_jitter(alpha=.5, size=.5)

Other options are to form 2d bins, count the number of observations in each, and color in proportion to the frequency.

p <- ggplot(data=Bikeshare, aes(x=casual, y=registered))
p+ geom_bin2d()

I prefer to make my colour scale light to dark:

p +  
  geom_bin2d() +
  scale_fill_gradient(low = "lightblue1", high = "steelblue4")

p +  
  geom_bin2d() +
  scale_fill_gradient(low = "lightblue1", high = "steelblue4" ,trans="log10")

The second plot takes a log of the counts so you get a better spread of colours

p +  
  stat_binhex() +
  scale_fill_gradient(low = "lightblue1", high = "steelblue4" ,trans="log10")

stat_binhex uses hexagonal binning

A more advanced plot fits a 2d density estimate and draw this as a contour plot.

p + stat_density2d()

# fills in the contours
p + stat_density2d(aes(fill = after_stat(level)), geom = "polygon") 

2.3 Scatterplot matrix

Back to the smaller dataset.

Go to articles tab for info https://ggobi.github.io/ggally/

library(GGally)
ggpairs(bike, columns=c("casual","registered","cnt" ))

You can change what is plotted on lower, upper and diagonal,using lower= etc. Putting in mapping= gives densities for both years separately

ggpairs(bike, mapping = aes(color = yr),
            columns=c("casual","registered","cnt" ),
  lower = list(continuous =  wrap("smooth", method="lm", se=F, alpha=.5)),
  diag = list(continuous = wrap("densityDiag", alpha=0.5 )))

ggpairs can handle factors as well. In this plot yrs is in as a plot variable, and as colour.

ggpairs(bike, mapping = aes(color = yr),
        columns=c("casual","registered","cnt","yr"),
        lower = list(continuous =  wrap("smooth", method="lm", se=F, alpha=.5)),
        diag = list(continuous = wrap("densityDiag", alpha=0.5 )))

2.4 Parallel coordinates

Construction:

s <- sample(nrow(bike),20)
ggparcoord(bike[s,], columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "yr",
           showPoints=TRUE,
           alphaLines=0)

ggparcoord(bike[s,], columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "yr",
           showPoints=TRUE,
           alphaLines=1)

ggparcoord(bike[s,], columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "yr")

By default, all variables are scaled to mean zero and standard deviation 1, individually. Variables that are positively correlated will have parallel line segments, here registered and cnt.

Other scaling options are:

ggparcoord(bike[s,], columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "yr", scale="uniminmax")

ggparcoord(bike[s,], columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "yr", scale="globalminmax")

In globalminmax, no scaling is done. Here you can see that most users per day are registered.

To see what certain patterns look like in a parallel coordinate plot, let us look at some fake data

x <- rnorm(100)
y <- x+ .3*rnorm(100)
z <- -y+ .1*rnorm(100)
w <- -z+ .5*rnorm(100)
ggparcoord(data.frame(x,y,z,w), scale="uniminmax")

Positive correlation exhibits as parallel segments.

Negative correlation has crossing. The stronger the negative correlation the smaller the pinch point.

ggparcoord(bike, columns=match(c("casual","registered","cnt"),names(bike)),
           groupColumn = "workingday",
           alphaLines=.4)

In the above we see that there is a difference in the casual-registered association for workingday or not. Both groups have positive association but slopes are different. There are more casual users at the weekend.

Verifying with a scatterplot

ggplot(data=bike, aes(x=casual, y=registered, color=workingday)) + geom_point()

The same pattern was evident in the full bike data.

2.5 Practice 3

Using the penguins dataset, make this plot

Which pair of variables and species has the highest correlation?

Can you find two variables with negative correlation, but positive correlation within each species? This is called Simpson’s paradox.

Using ggparcoord, plot all the numeric variables. Use species as color.

Can you identify presence of positive and negative correlation?

2.6 Missing data

Here we use package naniar. This is the dataset for your assignment. All the NAs are marked in grey.

m <- read_csv("marathon.csv",na=c(""," ","NA"))
vis_miss(m)

An upset plot shows the combination of missing values. Most of the NAs involve the club variable.

gg_miss_upset(m[,-c(22,20)])

Check out the visualisations in naniar for more options, eg the vignette at https://cran.r-project.org/web/packages/naniar/vignettes/naniar-visualisation.html

2.7 Titanic case study

There were 2201 people aboard the Titanic when it sank in 1912. A summary of the people on board and whether they survived or not is given in the Titanic dataset.

Here are some images as it left its last port of call, Cobh.

Can we figure out from this data which category of passenger had the highest chance of survival?

This case study looks at 4 categorical variables simultaneously: - Survival (yes or no), - class (first, second, third, crew,) - gender (male or female) and - age (child or adult).

First we look at the number of survivors by class

#Titanic  # a 4d array....
tit <- as.data.frame(Titanic)
head(tit)
##   Class    Sex   Age Survived Freq
## 1   1st   Male Child       No    0
## 2   2nd   Male Child       No    0
## 3   3rd   Male Child       No   35
## 4  Crew   Male Child       No    0
## 5   1st Female Child       No    0
## 6   2nd Female Child       No    0
tit<- tit[c(17:32,1:16),]

ggplot(data = tit, aes(x = Class, y = Freq,fill=Survived)) +
  geom_col() 

and then the survival rates by class

ggplot(data = tit, aes(x = Class, y = Freq,fill=Survived)) +
  geom_col(position="fill") 

  • There is a big difference in Survival rates across the classes.
  • First class passengers have the highest chance of survival, crew members the lowest.

We can bring in gender with facet_wrap

ggplot(data = tit, aes(x = Class, y = Freq,fill=Survived)) +
  geom_col(position="fill") + 
  facet_wrap( ~ Sex) + ylab("Rate")

  • Third class females do worse than crew females (46% versus 87%), and third class males do worse than crew males (17% versus 22%), although when gender is ignored the situation is reversed.

  • This apparent paradox (Simpson’s paradox) occurs because the vast majority of crew members are male.

  • The overall 3rd class survival rate of 25% is an average of 46% and 17%, but as there are 2.6 times more men than women in 3rd class 17% gets more weight.

  • The overall crew survival rate of 24% is an average of 87% and 22%, but as there are 37 times more men than women in crew class 87% gets very little weight.

Finally, bringing in age

ggplot(data = tit, aes(x = Class, y = Freq,fill=Survived)) +
  geom_col(position="fill") + 
  facet_wrap( ~ Age+ Sex) + ylab("Rate")

We can get some simplification by showing the survival rate, that is omit the red sections.

titR <- 
tit %>%  group_by(Class, Sex,Age) %>%
summarise(died=Freq[2], survived=Freq[1], rate=survived/sum(Freq))
titR
## # A tibble: 16 × 6
## # Groups:   Class, Sex [8]
##    Class Sex    Age    died survived     rate
##    <fct> <fct>  <fct> <dbl>    <dbl>    <dbl>
##  1 1st   Male   Child     0        5   1     
##  2 1st   Male   Adult   118       57   0.326 
##  3 1st   Female Child     0        1   1     
##  4 1st   Female Adult     4      140   0.972 
##  5 2nd   Male   Child     0       11   1     
##  6 2nd   Male   Adult   154       14   0.0833
##  7 2nd   Female Child     0       13   1     
##  8 2nd   Female Adult    13       80   0.860 
##  9 3rd   Male   Child    35       13   0.271 
## 10 3rd   Male   Adult   387       75   0.162 
## 11 3rd   Female Child    17       14   0.452 
## 12 3rd   Female Adult    89       76   0.461 
## 13 Crew  Male   Child     0        0 NaN     
## 14 Crew  Male   Adult   670      192   0.223 
## 15 Crew  Female Child     0        0 NaN     
## 16 Crew  Female Adult     3       20   0.870
ggplot(data = titR, aes(x = Class, y = rate)) +
  geom_col(fill="lightblue") + 
  facet_wrap( ~ Age+ Sex) 

2.8 Practice 4

Explore the survival rates for the data lusitania.csv on https://github.com/cbhurley/CRT2021vis This is a tabulated version of the data, in the same format as tit. The complete dataset is available on Kaggle.

You can find out more about the shipwreck https://en.wikipedia.org/wiki/RMS_Lusitania

Ci0tLQp0aXRsZTogIkRhdGEgdmlzdWFsaXNhdGlvbiBpbiBSIgphdXRob3I6ICJDYXRoZXJpbmUgSHVybGV5IgpkYXRlOiAiNy85LzIwMjEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBwYW5kb2NfYXJnczogWyItLW51bWJlci1vZmZzZXQ9MSJdCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQogIAotLS0KCjwhLS0gPHN0eWxlIHR5cGU9InRleHQvY3NzIj4gLS0+CjwhLS0gICBib2R5LCB0ZCB7IC0tPgo8IS0tICAgICBmb250LXNpemU6IDE0cHQ7IC0tPgo8IS0tICAgfSAtLT4KPCEtLSBjb2RlLnJ7IC0tPgo8IS0tICAgZm9udC1zaXplOiAxMnB0OyAtLT4KPCEtLSB9IC0tPgo8IS0tIHByZSB7IC0tPgo8IS0tICAgZm9udC1zaXplOiAxMnB0IC0tPgo8IS0tIH0gLS0+CjwhLS0gPC9zdHlsZT4gLS0+CgoKCgoKCgoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RikKYGBgCiMgUGFydCBJSSBBZHZhbmNlZCBkaXNwbGF5cwoKIyMgUHJlcGFyYXRpb24KCllvdSB3aWxsIGZpbmQgdGhlIG1hdGVyaWFsIGZvciB0aGlzIGxlY3R1cmUgYXQgPGh0dHBzOi8vZ2l0aHViLmNvbS9jYmh1cmxleS9DUlQyMDIxdmlzPgoKRmlyc3QgaW5zdGFsbCB0aGVzZSBwYWNrYWdlcwoKYGBge3IsIGV2YWw9Rn0KaW5zdGFsbC5wYWNrYWdlcyhjKCJHR2FsbHkiLCAiSVNMUjIiLCAibmFuaWFyIikpCmBgYAoKYW5kIGxvYWQgd2l0aAoKYGBge3J9CmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KElTTFIyKQpsaWJyYXJ5KHRpbWV0aykgIyBmb3IgdGhlIGRhdGEKbGlicmFyeShwYWxtZXJwZW5ndWlucykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobmFuaWFyKQoKYmlrZSA8LSBiaWtlX3NoYXJpbmdfZGFpbHkKYmlrZSRzZWFzb24gPC0gYygiV2ludGVyIiwiU3ByaW5nIiwgIlN1bW1lciIsIkZhbGwiKVtiaWtlJHNlYXNvbl0KYmlrZSRzZWFzb24gPC0gZmFjdG9yKGJpa2Ukc2Vhc29uLCBsZXZlbHMgPSBjKCJXaW50ZXIiLCJTcHJpbmciLCAiU3VtbWVyIiwiRmFsbCIpKSAgCgpiaWtlJG1udGggPC0gZmFjdG9yKGJpa2UkbW50aCkKCmJpa2UkaG9saWRheSA8LSBmYWN0b3IoYygiTm8iLCJZZXMiKVtiaWtlJGhvbGlkYXkrMV0pCmJpa2Ukd29ya2luZ2RheSA8LSBmYWN0b3IoYygiTm8iLCJZZXMiKVtiaWtlJHdvcmtpbmdkYXkrMV0pCgpiaWtlJHlyIDwtIGZhY3RvcihjKCIyMDExIiwiMjAxMiIpW2Jpa2UkeXIrMV0pCgpiaWtlJHdlYXRoZXJzaXQgPC0gYygiY2xlYXIiLCAiY2xvdWR5IiwgImxpZ2h0UCIsICJoZWF2eVAiKVtiaWtlJHdlYXRoZXJzaXRdCmJpa2Ukd2VhdGhlcnNpdCA8LSBmYWN0b3IoYmlrZSR3ZWF0aGVyc2l0LCBsZXZlbHM9IGMoImNsZWFyIiwgImNsb3VkeSIsICJsaWdodFAiLCAiaGVhdnlQIikpCmJpa2UgPC0gZHJvcGxldmVscyhiaWtlKQpgYGAKCgojIyBCaWcgZGF0YQoKSVNMUjIgaGFzIGFuIGhvdXJseSB2ZXJzaW9uIG9mIHRoZSBiaWtlIGRhdGEgd2l0aCA4NjQ1ICBvYnNlcnZhdGlvbnMuCgpUaGUgcGxvdCBvZiByZWdpc3RlcmVkIHZlcnN1c2Nhc3VhbCB1c2VycyBpcwoKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpICsgZ2VvbV9wb2ludCgpCmBgYAoKVGhlcmUgaXMgbGlrZWx5IGxvdCBvZiBvdmVycGxvdHRpbmcuCgpKaXR0ZXJpbmcgd2lsbCByYW5kb21seSB1bmRvIHJvdW5kaW5nLCBhbmQgdXNpbmcgYWxwaGEgd2lsbCBoZWxwIHRvbwoKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpICsgCiAgZ2VvbV9qaXR0ZXIoYWxwaGE9LjUsIHNpemU9LjUpCmBgYAoKT3RoZXIgb3B0aW9ucyBhcmUgdG8gZm9ybSAyZCBiaW5zLCBjb3VudCB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoLCBhbmQgY29sb3IgaW4KcHJvcG9ydGlvbiB0byB0aGUgZnJlcXVlbmN5LgoKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciJ9CnAgPC0gZ2dwbG90KGRhdGE9QmlrZXNoYXJlLCBhZXMoeD1jYXN1YWwsIHk9cmVnaXN0ZXJlZCkpCnArIGdlb21fYmluMmQoKQpgYGAKCkkgcHJlZmVyIHRvIG1ha2UgbXkgY29sb3VyIHNjYWxlIGxpZ2h0IHRvIGRhcms6CmBgYHtyLCAgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQpwICsgIAogIGdlb21fYmluMmQoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIpCgpwICsgIAogIGdlb21fYmluMmQoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIgLHRyYW5zPSJsb2cxMCIpCmBgYAoKVGhlIHNlY29uZCBwbG90IHRha2VzIGEgbG9nIG9mIHRoZSBjb3VudHMgc28geW91IGdldCBhIGJldHRlciBzcHJlYWQgb2YgY29sb3VycwoKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciJ9CnAgKyAgCiAgc3RhdF9iaW5oZXgoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAibGlnaHRibHVlMSIsIGhpZ2ggPSAic3RlZWxibHVlNCIgLHRyYW5zPSJsb2cxMCIpCmBgYApgc3RhdF9iaW5oZXhgIHVzZXMgaGV4YWdvbmFsIGJpbm5pbmcKCiBBIG1vcmUgYWR2YW5jZWQgcGxvdCBmaXRzIGEgMmQgZGVuc2l0eSBlc3RpbWF0ZSBhbmQgZHJhdyB0aGlzIGFzIGEgY29udG91ciBwbG90LgogCmBgYHtyLCAgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifSAKcCArIHN0YXRfZGVuc2l0eTJkKCkKYGBgICAKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciIsIGV2YWw9Rn0gCiMgZmlsbHMgaW4gdGhlIGNvbnRvdXJzCnAgKyBzdGF0X2RlbnNpdHkyZChhZXMoZmlsbCA9IGFmdGVyX3N0YXQobGV2ZWwpKSwgZ2VvbSA9ICJwb2x5Z29uIikgCmBgYCAgICAKCiMjIFNjYXR0ZXJwbG90IG1hdHJpeAoKQmFjayB0byB0aGUgc21hbGxlciBkYXRhc2V0LgoKR28gdG8gYXJ0aWNsZXMgdGFiIGZvciBpbmZvCjxodHRwczovL2dnb2JpLmdpdGh1Yi5pby9nZ2FsbHkvPgoKCmBgYHtyLCBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0iY2VudGVyIn0KbGlicmFyeShHR2FsbHkpCmdncGFpcnMoYmlrZSwgY29sdW1ucz1jKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiApKQpgYGAKCllvdSBjYW4gY2hhbmdlIHdoYXQgaXMgcGxvdHRlZCBvbiBsb3dlciwgdXBwZXIgYW5kIGRpYWdvbmFsLHVzaW5nCmBsb3dlcj1gIGV0Yy4KUHV0dGluZyBpbiBgbWFwcGluZz1gIGdpdmVzIGRlbnNpdGllcyBmb3IgYm90aCB5ZWFycyBzZXBhcmF0ZWx5CgoKYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQoKICAKZ2dwYWlycyhiaWtlLCBtYXBwaW5nID0gYWVzKGNvbG9yID0geXIpLAogICAgICAgICAgICBjb2x1bW5zPWMoImNhc3VhbCIsInJlZ2lzdGVyZWQiLCJjbnQiICksCiAgbG93ZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSAgd3JhcCgic21vb3RoIiwgbWV0aG9kPSJsbSIsIHNlPUYsIGFscGhhPS41KSksCiAgZGlhZyA9IGxpc3QoY29udGludW91cyA9IHdyYXAoImRlbnNpdHlEaWFnIiwgYWxwaGE9MC41ICkpKQoKYGBgCgoKYGdncGFpcnNgIGNhbiBoYW5kbGUgZmFjdG9ycyBhcyB3ZWxsLgpJbiB0aGlzIHBsb3QgYHlyc2AgaXMgaW4gYXMgYSBwbG90IHZhcmlhYmxlLCBhbmQgYXMgY29sb3VyLgoKYGBge3IsIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSJjZW50ZXIifQoKCmdncGFpcnMoYmlrZSwgbWFwcGluZyA9IGFlcyhjb2xvciA9IHlyKSwKICAgICAgICBjb2x1bW5zPWMoImNhc3VhbCIsInJlZ2lzdGVyZWQiLCJjbnQiLCJ5ciIpLAogICAgICAgIGxvd2VyID0gbGlzdChjb250aW51b3VzID0gIHdyYXAoInNtb290aCIsIG1ldGhvZD0ibG0iLCBzZT1GLCBhbHBoYT0uNSkpLAogICAgICAgIGRpYWcgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJkZW5zaXR5RGlhZyIsIGFscGhhPTAuNSApKSkKIApgYGAKCiMjIFBhcmFsbGVsIGNvb3JkaW5hdGVzCgpDb25zdHJ1Y3Rpb246CgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSJjZW50ZXIifQoKcyA8LSBzYW1wbGUobnJvdyhiaWtlKSwyMCkKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIiwKICAgICAgICAgICBzaG93UG9pbnRzPVRSVUUsCiAgICAgICAgICAgYWxwaGFMaW5lcz0wKQoKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIiwKICAgICAgICAgICBzaG93UG9pbnRzPVRSVUUsCiAgICAgICAgICAgYWxwaGFMaW5lcz0xKQoKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIikKYGBgCgpCeSBkZWZhdWx0LCBhbGwgdmFyaWFibGVzIGFyZSBzY2FsZWQgdG8gbWVhbiB6ZXJvIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gMSwgaW5kaXZpZHVhbGx5LgpWYXJpYWJsZXMgdGhhdCBhcmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpbGwgaGF2ZSBwYXJhbGxlbCBsaW5lIHNlZ21lbnRzLCBoZXJlIHJlZ2lzdGVyZWQgYW5kIGNudC4KCk90aGVyIHNjYWxpbmcgb3B0aW9ucyBhcmU6CgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSJjZW50ZXIifQoKZ2dwYXJjb29yZChiaWtlW3MsXSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInlyIiwgc2NhbGU9InVuaW1pbm1heCIpCmdncGFyY29vcmQoYmlrZVtzLF0sIGNvbHVtbnM9bWF0Y2goYygiY2FzdWFsIiwicmVnaXN0ZXJlZCIsImNudCIpLG5hbWVzKGJpa2UpKSwKICAgICAgICAgICBncm91cENvbHVtbiA9ICJ5ciIsIHNjYWxlPSJnbG9iYWxtaW5tYXgiKQoKYGBgCkluIGdsb2JhbG1pbm1heCwgbm8gc2NhbGluZyBpcyBkb25lLiBIZXJlIHlvdSBjYW4gc2VlIHRoYXQgbW9zdCB1c2VycyBwZXIgZGF5IGFyZSByZWdpc3RlcmVkLgoKVG8gc2VlIHdoYXQgY2VydGFpbiBwYXR0ZXJucyBsb29rIGxpa2UgaW4gYSBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QsIGxldAp1cyBsb29rIGF0IHNvbWUgZmFrZSBkYXRhCgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSJjZW50ZXIifQoKeCA8LSBybm9ybSgxMDApCnkgPC0geCsgLjMqcm5vcm0oMTAwKQp6IDwtIC15KyAuMSpybm9ybSgxMDApCncgPC0gLXorIC41KnJub3JtKDEwMCkKZ2dwYXJjb29yZChkYXRhLmZyYW1lKHgseSx6LHcpLCBzY2FsZT0idW5pbWlubWF4IikKYGBgCgpQb3NpdGl2ZSBjb3JyZWxhdGlvbiBleGhpYml0cyBhcyBwYXJhbGxlbCBzZWdtZW50cy4KCk5lZ2F0aXZlIGNvcnJlbGF0aW9uIGhhcyBjcm9zc2luZy4gVGhlIHN0cm9uZ2VyIHRoZSBuZWdhdGl2ZSBjb3JyZWxhdGlvbiB0aGUgc21hbGxlciB0aGUgcGluY2ggcG9pbnQuCgoKCgpgYGB7cixmaWcud2lkdGg9NiwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciJ9CmdncGFyY29vcmQoYmlrZSwgY29sdW1ucz1tYXRjaChjKCJjYXN1YWwiLCJyZWdpc3RlcmVkIiwiY250IiksbmFtZXMoYmlrZSkpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gIndvcmtpbmdkYXkiLAogICAgICAgICAgIGFscGhhTGluZXM9LjQpCmBgYAoKSW4gdGhlIGFib3ZlIHdlIHNlZSB0aGF0IHRoZXJlIGlzIGEgZGlmZmVyZW5jZSBpbiB0aGUgY2FzdWFsLXJlZ2lzdGVyZWQgYXNzb2NpYXRpb24gZm9yIHdvcmtpbmdkYXkgb3Igbm90LgpCb3RoIGdyb3VwcyBoYXZlIHBvc2l0aXZlIGFzc29jaWF0aW9uIGJ1dCBzbG9wZXMgYXJlIGRpZmZlcmVudC4KVGhlcmUgYXJlIG1vcmUgY2FzdWFsIHVzZXJzIGF0IHRoZSB3ZWVrZW5kLgoKVmVyaWZ5aW5nIHdpdGggYSBzY2F0dGVycGxvdAoKYGBge3IsICBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249ImNlbnRlciJ9CmdncGxvdChkYXRhPWJpa2UsIGFlcyh4PWNhc3VhbCwgeT1yZWdpc3RlcmVkLCBjb2xvcj13b3JraW5nZGF5KSkgKyBnZW9tX3BvaW50KCkKYGBgCgpUaGUgc2FtZSBwYXR0ZXJuIHdhcyBldmlkZW50IGluIHRoZSBmdWxsIGJpa2UgZGF0YS4KCgojIyBQcmFjdGljZSAzCgpVc2luZyB0aGUgYHBlbmd1aW5zYCBkYXRhc2V0LCBtYWtlIHRoaXMgcGxvdAoKYGBge3IsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTUsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GfQpnZ3BhaXJzKG5hLm9taXQocGVuZ3VpbnMpLCBtYXBwaW5nID0gYWVzKGNvbG9yID0gc3BlY2llcyksCiAgICAgICAgICAgIGNvbHVtbnM9YygzOjcpLAogIGxvd2VyID0gbGlzdChjb250aW51b3VzID0gIHdyYXAoInNtb290aCIsIG1ldGhvZD0ibG0iLCBzZT1GLCBhbHBoYT0uNSkpLAogIGRpYWcgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJkZW5zaXR5RGlhZyIsIGFscGhhPTAuNSApKSkKYGBgCldoaWNoIHBhaXIgb2YgdmFyaWFibGVzIGFuZCBzcGVjaWVzIGhhcyB0aGUgaGlnaGVzdCBjb3JyZWxhdGlvbj8KCkNhbiB5b3UgZmluZCB0d28gdmFyaWFibGVzIHdpdGggbmVnYXRpdmUgY29ycmVsYXRpb24sIGJ1dCBwb3NpdGl2ZSBjb3JyZWxhdGlvbiB3aXRoaW4gZWFjaCBzcGVjaWVzPwpUaGlzIGlzIGNhbGxlZCBTaW1wc29uJ3MgcGFyYWRveC4KClVzaW5nIGBnZ3BhcmNvb3JkYCwgcGxvdCBhbGwgdGhlIG51bWVyaWMgdmFyaWFibGVzLiBVc2Ugc3BlY2llcyBhcyBjb2xvci4KCkNhbiB5b3UgaWRlbnRpZnkgcHJlc2VuY2Ugb2YgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGNvcnJlbGF0aW9uPwoKCmBgYHtyLGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTMsIGZpZy5hbGlnbj0iY2VudGVyIiwgZWNobz1GLCBldmFsPUZ9CgoKZ2dwYXJjb29yZChwZW5ndWlucywgY29sdW1ucz13aGljaChzYXBwbHkocGVuZ3VpbnMsIGlzLm51bWVyaWMpKSwKICAgICAgICAgICBncm91cENvbHVtbiA9ICJzcGVjaWVzIiwKICAgICAgICAgICBhbHBoYUxpbmVzPS40KQoKZ2dwYXJjb29yZChwZW5ndWlucywgY29sdW1ucz13aGljaChzYXBwbHkocGVuZ3VpbnMsIGlzLm51bWVyaWMpKSwKICAgICAgICAgICBncm91cENvbHVtbiA9ICJpc2xhbmQiLAogICAgICAgICAgIGFscGhhTGluZXM9LjQpCgpnZ3BhcmNvb3JkKHBlbmd1aW5zLCBjb2x1bW5zPXdoaWNoKHNhcHBseShwZW5ndWlucywgaXMubnVtZXJpYykpLAogICAgICAgICAgIGdyb3VwQ29sdW1uID0gInNleCIsCiAgICAgICAgICAgYWxwaGFMaW5lcz0uNCkKCmBgYAoKIyMgTWlzc2luZyBkYXRhCgpIZXJlIHdlIHVzZSBwYWNrYWdlIG5hbmlhci4KVGhpcyBpcyB0aGUgZGF0YXNldCBmb3IgeW91ciBhc3NpZ25tZW50LiBBbGwgdGhlIE5BcyBhcmUgbWFya2VkIGluIGdyZXkuCgpgYGB7cixmaWcuYWxpZ249ImNlbnRlciJ9Cm0gPC0gcmVhZF9jc3YoIm1hcmF0aG9uLmNzdiIsbmE9YygiIiwiICIsIk5BIikpCnZpc19taXNzKG0pCmBgYAoKQW4gdXBzZXQgcGxvdCBzaG93cyB0aGUgY29tYmluYXRpb24gb2YgbWlzc2luZyB2YWx1ZXMuCk1vc3Qgb2YgdGhlIE5BcyBpbnZvbHZlIHRoZSBjbHViIHZhcmlhYmxlLgoKYGBge3IsZmlnLmFsaWduPSJjZW50ZXIifQpnZ19taXNzX3Vwc2V0KG1bLC1jKDIyLDIwKV0pCmBgYAoKQ2hlY2sgb3V0IHRoZSB2aXN1YWxpc2F0aW9ucyBpbiBgbmFuaWFyYCBmb3IgbW9yZSBvcHRpb25zLCBlZyB0aGUgdmlnbmV0dGUgYXQgPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9uYW5pYXIvdmlnbmV0dGVzL25hbmlhci12aXN1YWxpc2F0aW9uLmh0bWw+CgoKIyMgVGl0YW5pYyBjYXNlIHN0dWR5CgpUaGVyZSB3ZXJlIDIyMDEgcGVvcGxlIGFib2FyZCB0aGUgVGl0YW5pYyB3aGVuIGl0IHNhbmsgaW4gMTkxMi4KQSBzdW1tYXJ5IG9mIHRoZSBwZW9wbGUgb24gYm9hcmQgYW5kIHdoZXRoZXIgdGhleSBzdXJ2aXZlZCBvciBub3QgaXMgZ2l2ZW4gaW4gdGhlIFRpdGFuaWMgZGF0YXNldC4KCkhlcmUgYXJlIHNvbWUgaW1hZ2VzIGFzIGl0IGxlZnQgaXRzIGxhc3QgcG9ydCBvZiBjYWxsLCBDb2JoLgoKYGBge3IsIGVjaG89RiwgZXZhbD1ULCAgb3V0LndpZHRoPSIzMCUiLGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLnNob3c9J2hvbGQnfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhjKCJmaWdzL3RpdGFuaWMucG5nIikpCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGMoImZpZ3MvY29iaC5wbmciKSkKYGBgCgoKQ2FuIHdlIGZpZ3VyZSBvdXQgZnJvbSB0aGlzIGRhdGEgd2hpY2ggY2F0ZWdvcnkgb2YgcGFzc2VuZ2VyIGhhZCB0aGUgaGlnaGVzdCBjaGFuY2Ugb2Ygc3Vydml2YWw/CgpUaGlzIGNhc2Ugc3R1ZHkgbG9va3MgYXQgNCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgc2ltdWx0YW5lb3VzbHk6Ci0gU3Vydml2YWwgKHllcyBvciBubyksIAotIGNsYXNzIChmaXJzdCwgc2Vjb25kLCB0aGlyZCwgY3JldywpCi0gZ2VuZGVyIChtYWxlIG9yIGZlbWFsZSkgYW5kIAotIGFnZSAoY2hpbGQgb3IgYWR1bHQpLgoKCkZpcnN0IHdlIGxvb2sgYXQgdGhlIG51bWJlciBvZiBzdXJ2aXZvcnMgYnkgY2xhc3MKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTMsIGZpZy5hbGlnbj0iY2VudGVyIn0KI1RpdGFuaWMgICMgYSA0ZCBhcnJheS4uLi4KdGl0IDwtIGFzLmRhdGEuZnJhbWUoVGl0YW5pYykKaGVhZCh0aXQpCnRpdDwtIHRpdFtjKDE3OjMyLDE6MTYpLF0KCmdncGxvdChkYXRhID0gdGl0LCBhZXMoeCA9IENsYXNzLCB5ID0gRnJlcSxmaWxsPVN1cnZpdmVkKSkgKwogIGdlb21fY29sKCkgCmBgYAoKYW5kIHRoZW4gdGhlIHN1cnZpdmFsIHJhdGVzIGJ5IGNsYXNzCgpgYGB7ciwgIGZpZy53aWR0aD00LjUsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSJjZW50ZXIifQpnZ3Bsb3QoZGF0YSA9IHRpdCwgYWVzKHggPSBDbGFzcywgeSA9IEZyZXEsZmlsbD1TdXJ2aXZlZCkpICsKICBnZW9tX2NvbChwb3NpdGlvbj0iZmlsbCIpIApgYGAKCi0gVGhlcmUgaXMgYSBiaWcgZGlmZmVyZW5jZSBpbiBTdXJ2aXZhbCByYXRlcyBhY3Jvc3MgdGhlIGNsYXNzZXMuCi0gRmlyc3QgY2xhc3MgcGFzc2VuZ2VycyBoYXZlIHRoZSBoaWdoZXN0IGNoYW5jZSBvZiBzdXJ2aXZhbCwgY3JldyBtZW1iZXJzIHRoZSBsb3dlc3QuIAoKCldlIGNhbiBicmluZyBpbiBnZW5kZXIgd2l0aCBgZmFjZXRfd3JhcGAKCmBgYHtyLCAgZmlnLndpZHRoPTUuNSwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249ImNlbnRlciJ9CmdncGxvdChkYXRhID0gdGl0LCBhZXMoeCA9IENsYXNzLCB5ID0gRnJlcSxmaWxsPVN1cnZpdmVkKSkgKwogIGdlb21fY29sKHBvc2l0aW9uPSJmaWxsIikgKyAKICBmYWNldF93cmFwKCB+IFNleCkgKyB5bGFiKCJSYXRlIikKYGBgCgotIFRoaXJkIGNsYXNzIGZlbWFsZXMgZG8gd29yc2UgdGhhbiBjcmV3IGZlbWFsZXMgKDQ2JSB2ZXJzdXMgODclKSwgYW5kCnRoaXJkIGNsYXNzIG1hbGVzIGRvIHdvcnNlIHRoYW4gY3JldyBtYWxlcyAoMTclIHZlcnN1cyAyMiUpLCBhbHRob3VnaCB3aGVuIGdlbmRlciBpcyBpZ25vcmVkIHRoZSBzaXR1YXRpb24gaXMKcmV2ZXJzZWQuCgotIFRoaXMgYXBwYXJlbnQgcGFyYWRveCAoU2ltcHNvbidzIHBhcmFkb3gpIG9jY3VycyBiZWNhdXNlIHRoZSB2YXN0IG1ham9yaXR5IG9mIGNyZXcgbWVtYmVycyBhcmUgbWFsZS4KCi0gVGhlIG92ZXJhbGwgM3JkIGNsYXNzIHN1cnZpdmFsIHJhdGUgb2YgMjUlIGlzIGFuIGF2ZXJhZ2Ugb2YgNDYlIGFuZCAxNyUsIGJ1dCBhcyB0aGVyZSBhcmUgMi42IHRpbWVzCm1vcmUgbWVuIHRoYW4gd29tZW4gaW4gM3JkIGNsYXNzIDE3JSBnZXRzIG1vcmUgd2VpZ2h0LgoKLSBUaGUgb3ZlcmFsbCBjcmV3IHN1cnZpdmFsIHJhdGUgb2YgMjQlIGlzIGFuIGF2ZXJhZ2Ugb2YgODclIGFuZCAyMlwlLCBidXQgYXMgdGhlcmUgYXJlIDM3IHRpbWVzCm1vcmUgbWVuIHRoYW4gd29tZW4gaW4gY3JldyBjbGFzcyA4NyUgZ2V0cyB2ZXJ5IGxpdHRsZSB3ZWlnaHQuCgpGaW5hbGx5LCBicmluZ2luZyBpbiBhZ2UKYGBge3IsICBmaWcud2lkdGg9NS41LCBmaWcuaGVpZ2h0PTQuNSwgZmlnLmFsaWduPSJjZW50ZXIifQpnZ3Bsb3QoZGF0YSA9IHRpdCwgYWVzKHggPSBDbGFzcywgeSA9IEZyZXEsZmlsbD1TdXJ2aXZlZCkpICsKICBnZW9tX2NvbChwb3NpdGlvbj0iZmlsbCIpICsgCiAgZmFjZXRfd3JhcCggfiBBZ2UrIFNleCkgKyB5bGFiKCJSYXRlIikKYGBgCgpXZSBjYW4gZ2V0IHNvbWUgc2ltcGxpZmljYXRpb24gYnkgc2hvd2luZyB0aGUgc3Vydml2YWwgcmF0ZSwgdGhhdCBpcyBvbWl0IHRoZSByZWQgc2VjdGlvbnMuCgoKYGBge3IsICBmaWcud2lkdGg9NC41LCBmaWcuaGVpZ2h0PTQuNSwgZmlnLmFsaWduPSJjZW50ZXIifQp0aXRSIDwtIAp0aXQgJT4lICBncm91cF9ieShDbGFzcywgU2V4LEFnZSkgJT4lCnN1bW1hcmlzZShkaWVkPUZyZXFbMl0sIHN1cnZpdmVkPUZyZXFbMV0sIHJhdGU9c3Vydml2ZWQvc3VtKEZyZXEpKQp0aXRSCgpnZ3Bsb3QoZGF0YSA9IHRpdFIsIGFlcyh4ID0gQ2xhc3MsIHkgPSByYXRlKSkgKwogIGdlb21fY29sKGZpbGw9ImxpZ2h0Ymx1ZSIpICsgCiAgZmFjZXRfd3JhcCggfiBBZ2UrIFNleCkgCgpgYGAKCiMjIFByYWN0aWNlIDQKCkV4cGxvcmUgdGhlIHN1cnZpdmFsIHJhdGVzIGZvciB0aGUgZGF0YSBgbHVzaXRhbmlhLmNzdmAgb24gPGh0dHBzOi8vZ2l0aHViLmNvbS9jYmh1cmxleS9DUlQyMDIxdmlzPgpUaGlzIGlzIGEgdGFidWxhdGVkIHZlcnNpb24gb2YgdGhlIGRhdGEsIGluIHRoZSBzYW1lIGZvcm1hdCBhcyBgdGl0YC4gVGhlIGNvbXBsZXRlIGRhdGFzZXQKaXMgYXZhaWxhYmxlIG9uIEthZ2dsZS4KCllvdSBjYW4gZmluZCBvdXQgbW9yZSBhYm91dCB0aGUgc2hpcHdyZWNrIDxodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9STVNfTHVzaXRhbmlhPgoKCgojIyBSZWNvbW1lbmRlZCBib29rCgoiVGhlIHBsb3QiLCBKZWFuIEhhbmZmIEtvcmVsaXR6CgpgYGB7ciwgZWNobz1GLCBldmFsPVQsIG91dC53aWR0aD0iMjUlIiwgZmlnLmFsaWduPSdjZW50ZXInfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiZmlncy9wbG90LnBuZyIpCmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIGV2YWw9RkFMU0UsIHB1cmw9RkFMU0V9CmtuaXRyOjpwdXJsKCJsZWN0Mi5SbWQiKQoKYGBgCgoK